package model

import (
	"encoding/json"
	"time"
	"errors"
	"strconv"
	"strings"
	"net/http"
	"io/ioutil"
	"api/util"
)

type (
	Withdraw struct {
		Id          		int    	`json:"id"`
		Phone       		string 	`json:"phone"`
		Amount       		int    	`json:"amount"`
		Name        		string 	`json:"name"`
		Bank        		string 	`json:"bank"`
		BankNo      		string	`json:"bank_no"`
		Status      		int    	`json:"status"`
		PromotionOfficerId 	int 	`json:"promotion_officer_id"`
		CreatedDate 		int    	`json:"created_date"`
		UpdatedDate 		int    	`json:"updated_date"`
		BatchId             string  `json:"batch_id"`
		State               int     `json:"state"`
	}
	IncomeDetails struct {
		Id              		int			`json:"id"`
		ProductId       		int     	`json:"product_id"`
		ProductType     		int     	`json:"product_type"`
		ProductName     		string 		`json:"product_name"`
		Income					int         `json:"income"`
		CreatedDate 			int     	`json:"created_date"`
	}
	WithdrawRecord struct {
		Id          		int    	`json:"id"`
		Amount       		int    	`json:"amount"`
		Status      		int    	`json:"status"`
		CreatedDate 		int    	`json:"created_date"`
	}
	Pay struct {
		BatchId			string				`json:"batch_id"`
		SubAccount		string				`json:"sub_account"`
		PayLists		[] *PayListsItem 	`json:"pay_lists"`
		TemplateId     	string              `json:"template_id"`
	}

	PayListsItem struct {
		OrderId		string		`json:"order_id"`
		Name		string		`json:"name"`
		IdCard		string		`json:"id_card"`
		BankNo		string		`json:"bank_no"`
		BankName	string		`json:"bank_name"`
		Mobile		string		`json:"mobile"`
		Amount		int			`json:"amount"`
	}
)

func redisWithdraw() (ret []*Withdraw, err error) {
	err = rdb.Del(ctx, "withdraw").Err()
	if err != nil {
		return ret, err
	}

	rows, err := db.Query("SELECT * FROM withdraw ORDER BY updated_date DESC, status DESC")
	if err != nil {
		return ret, err
	}

	defer rows.Close()

	for rows.Next() {
		w := Withdraw{}
		err := rows.Scan(&w.Id, &w.Phone, &w.Amount, &w.Name, &w.Bank, &w.BankNo, &w.Status, &w.PromotionOfficerId, &w.CreatedDate, &w.UpdatedDate, &w.BatchId, &w.State)
		if err != nil {
			return ret, err
		}

		ret = append(ret, &w)

		jsonStu, err := json.Marshal(w)
		if err != nil {
			return ret, err
		}
		err = rdb.RPush(ctx, "withdraw", jsonStu).Err()
		if err != nil {
			return ret, err
		}
	}

	return ret, nil
}

func (p *Withdraw) QueryWithdraw(status string, phone string) (ret []*Withdraw, err error) {
	sql1 := "SELECT * FROM withdraw WHERE"
	if len(status) > 0 && len(phone) == 0 {
		sql1 += " status = " + status
	} else if len(status) == 0 && len(phone) > 0 {
		sql1 += " phone = " + phone
	} else {
		sql1 += " status = " + status + " AND phone = " + phone
	}

	sql1 += " ORDER BY updated_date DESC"

	rows, err := db.Query(sql1)
	if err != nil {
		return ret, err
	}

	for rows.Next() {
		w := Withdraw{}
		err := rows.Scan(&w.Id, &w.Phone, &w.Amount, &w.Name, &w.Bank, &w.BankNo, &w.Status, &w.PromotionOfficerId, &w.CreatedDate, &w.UpdatedDate, &w.BatchId, &w.State)
		if err != nil {
			return ret, err
		}
		ret = append(ret, &w)
	}

	defer rows.Close()

	return ret, nil
}

func (p *Withdraw) GetWithdraw() (ret []*Withdraw, err error) {
	vals, err := rdb.LRange(ctx, "withdraw", 0, -1).Result()
	if err != nil {
		return nil, err
	}

	if len(vals) > 0 {
		for _, v := range vals {
			w := Withdraw{}
			err := json.Unmarshal([]byte(v), &w)
			if err != nil {
				return ret, err
			}

			ret = append(ret, &w)
		}

		return ret, nil
	}

	ret, err = redisWithdraw()
	if err != nil {
		return ret, err
	}

	return ret, nil
}

func (w *Withdraw) CreateWithdraw(wd Withdraw, cid int) error {
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare("SELECT promotion_id FROM customer WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	var pid int
	err = stmt.QueryRow(cid).Scan(&pid)
	if err != nil {
		tx.Rollback()
		return err
	}

	stmt0, err := tx.Prepare("SELECT status FROM promotion_officer WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	var status int

	err = stmt0.QueryRow(pid).Scan(&status)
	if err != nil {
		tx.Rollback()
		return err
	}

	if status == 0 {
		tx.Rollback()
		return errors.New("因违规操作，您的账号暂时被锁定")
	}

	stmt1, err := tx.Prepare("INSERT withdraw SET phone=?, amount=?, name=?, bank=?, bank_no=?, status=?, promotion_officer_id=?, created_date=?, updated_date=?, batch_id=?, state=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	_, err = stmt1.Exec(wd.Phone, wd.Amount, wd.Name, wd.Bank, wd.BankNo, 0, pid, time.Now().Unix(), time.Now().Unix(), "", 0)
	if err != nil {
		tx.Rollback()
		return err
	}

	stmt2, err := tx.Prepare("SELECT withdrawable FROM promotion_officer WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	var withdrawable int
	err = stmt2.QueryRow(pid).Scan(&withdrawable)
	if err != nil {
		tx.Rollback()
		return err
	}

	if withdrawable < wd.Amount {
		tx.Rollback()
		return errors.New("提现需小于可提现")
	}

	withdrawable -= wd.Amount

	stmt3, err := tx.Prepare("UPDATE promotion_officer SET withdrawable=?, updated_date=? WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	_, err = stmt3.Exec(withdrawable, time.Now().Unix(), pid)
	if err != nil {
		tx.Rollback()
		return err
	}

	tx.Commit()

	_, err = redisPromotionOfficer()
	if err != nil {
		return err
	}

	_, err = redisWithdraw()
	if err != nil {
		return err
	}

	return nil
}

func withdrawApproval(id int, status int) error {
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare("SELECT phone, amount, name, bank, bank_no FROM withdraw WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	w := Withdraw{}
	err = stmt.QueryRow(id).Scan(&w.Phone, &w.Amount, &w.Name, &w.Bank, &w.BankNo)
	if err != nil {
		tx.Rollback()
		return err
	}

	params1 := util.GetParams(`{"uuid":"` + util.MERID + w.Phone + `"}`)
	resp1, err := http.Post(util.ADDR + `/api/maker_auth_info.api`, "application/json", params1)
	if err != nil {
		tx.Rollback()
		return err		
	}
	defer resp1.Body.Close()

	respBody1, err := ioutil.ReadAll(resp1.Body)
	if err != nil {
		return err
	}
	respString1 := string(respBody1)
	respMap1 := make(map[string]interface{})

	err = json.Unmarshal([]byte(respString1), &respMap1)
	if err != nil {
		return err
	}

	statusCode := int(respMap1["status_code"].(float64))

	var infoIdCard, infoMobile string
	if (statusCode == 200) {
		infoIdCard = respMap1["data"].(map[string]interface{})["id_card"].(string)
		infoMobile = respMap1["data"].(map[string]interface{})["mobile"].(string)
	} else {
		return errors.New("调用第三方接口失败 -> /api/maker_auth_info.api")
	}

	pay := Pay{}
	pay.BatchId = w.Phone + time.Now().Format("20060102150405")
	pay.SubAccount = "30206582115977"
	item := PayListsItem{}
	item.OrderId = util.MERID + w.Phone + time.Now().Format("20060102150405")
	item.Name = w.Name
	item.IdCard = infoIdCard
	item.BankNo = w.BankNo
	item.BankName = w.Bank
	item.Mobile = infoMobile
	item.Amount = w.Amount
	pay.PayLists = append(pay.PayLists, &item)
	pay.TemplateId = "T1670655386704"

	jsonByte, _ := json.Marshal(pay)
	jsonString := string(jsonByte)
	params2 := util.GetParams(jsonString)

	resp2, err := http.Post(util.ADDR + `/api/sync_orders.api`, "application/json", params2)
	if err != nil {
		tx.Rollback()
		return err		
	}
	defer resp2.Body.Close()

	respBody2, err := ioutil.ReadAll(resp2.Body)
	if err != nil {
		tx.Rollback()
		return err
	}
	respString2 := string(respBody2)
	respMap2 := make(map[string]interface{})

	err = json.Unmarshal([]byte(respString2), &respMap2)
	if err != nil {
		tx.Rollback()
		return err
	}

	statusCode = int(respMap2["status_code"].(float64))
	if (statusCode != 200) {
		tx.Rollback()
		return errors.New("调用第三方接口失败 -> /api/sync_orders.api")
	}

	batchId := respMap2["data"].(map[string]interface{})["batch_id"]
	failNum := int(respMap2["data"].(map[string]interface{})["fail_num"].(float64))
	successNum := int(respMap2["data"].(map[string]interface{})["success_num"].(float64))

	if failNum == 0 && successNum == 1 {
		stmt1, err := tx.Prepare("UPDATE withdraw SET status=?, updated_date=?, batch_id=?, state=? WHERE id=?")
		if err != nil {
			tx.Rollback()
			return err
		}

		_, err = stmt1.Exec(status, time.Now().Unix(), batchId, 0, id)
		if err != nil {
			tx.Rollback()
			return err
		}

		tx.Commit()

		_, err = redisWithdraw()
		if err != nil {
			return err
		}
		return nil
	} else {
		return errors.New("调用第三方接口失败 -> /api/sync_orders.api")
	}
}

func withdrawReject(id int, status int) error {
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	stmt1, err := tx.Prepare("SELECT amount, promotion_officer_id FROM withdraw WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	var amount, pid int
	err = stmt1.QueryRow(id).Scan(&amount, &pid)
	if err != nil {
		tx.Rollback()
		return err
	}

	stmt2, err := tx.Prepare("SELECT withdrawable FROM promotion_officer WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	var withdrawable int
	err = stmt2.QueryRow(pid).Scan(&withdrawable)
	if err != nil {
		tx.Rollback()
		return err
	}

	withdrawable += amount

	stmt3, err := tx.Prepare("UPDATE promotion_officer SET withdrawable=?, updated_date=? WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	_, err = stmt3.Exec(withdrawable, time.Now().Unix(), pid)
	if err != nil {
		tx.Rollback()
		return err
	}

	stmt4, err := tx.Prepare("UPDATE withdraw SET status=?, updated_date=?, batch_id=?, state=? WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	_, err = stmt4.Exec(status, time.Now().Unix(), "", 0, id)
	if err != nil {
		tx.Rollback()
		return err
	}

	tx.Commit()

	_, err = redisPromotionOfficer()
	if err != nil {
		return err
	}

	_, err = redisWithdraw()
	if err != nil {
		return err
	}

	return nil
}

func (s *Withdraw) UpdateWithdraw(id int, status int) error {
	if status == 1 {
		err := withdrawReject(id, status)
		if err != nil {
			return err
		}
		return nil
	} else if status == 2 {
		err := withdrawApproval(id, status)
		if err != nil {
			return err
		}
		return nil
	}

	return errors.New("参数status错误！")
}

func (s *Withdraw) UpdateWithdrawState(id int, batchId string) error {
	params := util.GetParams(`{"batch_id":` + batchId + `,"size":1,"page":1}`)

	resp, err := http.Post(util.ADDR + `/api/get_orders_status.api`, "application/json", params)
	if err != nil {
		return err		
	}

	defer resp.Body.Close()

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	respString := string(respBody)

	respMap := make(map[string]interface{})

	err = json.Unmarshal([]byte(respString), &respMap)
	if err != nil {
		return err
	}

	statusCode := int(respMap["status_code"].(float64))
	if (statusCode != 200) {
		return errors.New("调用第三方接口失败 -> /api/get_orders_status.api")
	}

	sf := respMap["data"].(map[string]interface{})["query_items"].([]interface{})[0].(map[string]interface{})["state"]

	state := int(sf.(float64))

	if state == 3 {
		d := `{"batch_id":"` + batchId + `","type":1,"invoice_id":1,"company_name":"上海晴天网络科技有限公司","organization":"310110066046896","company_address":"上海市杨浦区国定路346号四楼0820室","bank":"招商银行股份有限公司上海长阳支行","bank_no":"121910846110303","company_office":"13681727651","receiving_address":"上海市浦东新区莲中路255弄丰巢","receiving_name":"杨勇","mobile":"13681727651","invoice_type":1,"email":"13681727651@163.com"}`

		params = util.GetParams(d)

		resp, err = http.Post(util.ADDR + `/api/invoices.api`, "application/json", params)
		if err != nil {
			return err		
		}

		defer resp.Body.Close()
	}

	stmt4, err := db.Prepare("UPDATE withdraw SET updated_date=?, state=? WHERE id=?")
	if err != nil {
		return err
	}

	_, err = stmt4.Exec(time.Now().Unix(), state, id)
	if err != nil {
		return err
	}

	_, err = redisWithdraw()
	if err != nil {
		return err
	}

	return nil
}

func recursiveQueryIncome(pid int, isSelf bool) (ret []*IncomeDetails, err error){
	var sql1 string

	if isSelf == true {
		sql1 = "SELECT id, product_id, product_type, promotion_officer_income, created_date FROM order_list WHERE promotion_officer_id=" + strconv.Itoa(pid) + " ORDER BY id"
	} else {
		sql1 = "SELECT id, product_id, product_type, master_income, created_date FROM order_list WHERE promotion_officer_id=" + strconv.Itoa(pid) + " ORDER BY id"
	}

	rows, err := db.Query(sql1)
	if err != nil {
		return ret, err
	}

	defer rows.Close()

	for rows.Next() {
		i := IncomeDetails{}
		if err := rows.Scan(&i.Id, &i.ProductId, &i.ProductType, &i.Income, &i.CreatedDate); err != nil {
			return ret, err
		}

		if i.ProductType == 1 {
			stmt1, err := db.Prepare("SELECT name FROM securities_company WHERE id=?")
			if err != nil {
				return ret, err
			}
			err = stmt1.QueryRow(i.ProductId).Scan(&i.ProductName)
			if err != nil {
				return ret, err
			}
		} else if i.ProductType == 2 {
			stmt2, err := db.Prepare("SELECT name FROM credit_card WHERE id=?")
			if err != nil {
				return ret, err
			}
			err = stmt2.QueryRow(i.ProductId).Scan(&i.ProductName)
			if err != nil {
				return ret, err
			}
		} else {
			return ret, errors.New("order表存在错误")
		}

		ret = append(ret, &i)
	}

	return ret, nil
}

func (w *Withdraw) GetIncomeRecord(cid int) (ret []*IncomeDetails, err error) {
	stmt, err := db.Prepare("SELECT promotion_id FROM customer WHERE id=?")
	if err != nil {
		return ret, err
	}

	var pid int
	err = stmt.QueryRow(cid).Scan(&pid)
	if err != nil {
		return ret, err
	}

	selfRecord, err := recursiveQueryIncome(pid, true)
	if err != nil {
		return ret, err
	}

	ret = append(ret, selfRecord...)

	stmt1, err := db.Prepare("SELECT apprentices FROM promotion_officer WHERE id=?")
	if err != nil {
		return ret, err
	}

	var apprentices string
	err = stmt1.QueryRow(pid).Scan(&apprentices)
	if err != nil {
		return ret, err
	}

	if len(apprentices) > 0 {
		s := strings.Split(apprentices, ",")
		for _, v := range s {
			vInt, err := strconv.Atoi(v)
			if err != nil {
				return nil, err
			}

			apprenticesRecord, err := recursiveQueryIncome(vInt, false)
			if err != nil {
				return ret, err
			}

			ret = append(ret, apprenticesRecord...)
		}
	}

	return ret, nil
}

func (w *Withdraw) GetWithdrawRecord(cid int) (ret []*WithdrawRecord, err error) {
	stmt, err := db.Prepare("SELECT promotion_id FROM customer WHERE id=?")
	if err != nil {
		return ret, err
	}

	var pid int
	err = stmt.QueryRow(cid).Scan(&pid)
	if err != nil {
		return ret, err
	}

	sql1 := "SELECT id, amount, status, created_date FROM withdraw WHERE promotion_officer_id=" + strconv.Itoa(pid) + " ORDER BY updated_date DESC"

	rows, err := db.Query(sql1)
	if err != nil {
		return ret, err
	}

	defer rows.Close()

	for rows.Next() {
		wr := WithdrawRecord{}
		if err := rows.Scan(&wr.Id, &wr.Amount, &wr.Status, &wr.CreatedDate); err != nil {
			return ret, err
		}

		ret = append(ret, &wr)
	}

	return ret, nil
}